package com.hruiworks.campusgroup.service.impl;

import com.hruiworks.campusgroup.enums.ErrorCode;
import com.hruiworks.campusgroup.exception.BusinessException;
import com.hruiworks.campusgroup.pojo.entity.User;
import com.hruiworks.campusgroup.service.SseService;
import com.hruiworks.campusgroup.sse.DefaultManagedEmitterFactory;
import com.hruiworks.campusgroup.sse.ManagedEmitter;
import com.hruiworks.campusgroup.utils.RedisUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import java.io.IOException;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

import static com.hruiworks.campusgroup.constants.redisKey.UniversityRedisKey.UNIVERSITY_ALL;

@Service
@Slf4j
public class SseServiceImpl implements SseService {

    /**
     * 维护多链接
     */
    private final ConcurrentHashMap<Integer, ConcurrentHashMap<String, ManagedEmitter>> emitters = new ConcurrentHashMap<>();

    @Override
    public SseEmitter sseConnect(Integer schoolKey, String key) {
        DefaultManagedEmitterFactory defaultManagedEmitterFactory = new DefaultManagedEmitterFactory();
        ManagedEmitter managedEmitter = defaultManagedEmitterFactory.getEmitter(() -> {
            ConcurrentHashMap<String, ManagedEmitter> schoolMap = emitters.get(schoolKey);
            schoolMap.remove(key);
            if (schoolMap.isEmpty()) {
                emitters.remove(schoolKey);
            }
            log.info(String.valueOf(emitters.size()));
            log.info("连接关闭");
        });
        ConcurrentHashMap<String, ManagedEmitter> userMap = emitters.computeIfAbsent(schoolKey, integer -> new ConcurrentHashMap<>());
        ManagedEmitter alreadyEmitter = userMap.get(key);
        if (!Objects.isNull(alreadyEmitter)) {
            // todo 目前只允许单设备sse，故需要中断旧连接的心跳线程
            alreadyEmitter.heartbeatInterrupt("主动中断");
        }
        userMap.put(key, managedEmitter);
        log.info(String.valueOf(userMap.size()));
        log.info("新连接");
        return managedEmitter.getEmitter();
    }

    @Override
    public SseEmitter sseConnect(User user) {
        Integer schoolKey = (Integer) RedisUtils.hashGet(UNIVERSITY_ALL, user.getUniversity());
        return sseConnect(schoolKey, generateKey(user));
    }

    @Override
    public void disconnect(User user) {
        Integer schoolKey = (Integer) RedisUtils.hashGet(UNIVERSITY_ALL, user.getUniversity());
        ConcurrentHashMap<String, ManagedEmitter> schoolMap = emitters.get(schoolKey);
        if (Objects.isNull(schoolMap)) {
            return;
        }
        ManagedEmitter managedEmitter = schoolMap.get(generateKey(user));
        if (!Objects.isNull(managedEmitter)) {
            schoolMap.remove(generateKey(user));
            if (schoolMap.isEmpty()) {
                emitters.remove(schoolKey);
            }
            managedEmitter.heartbeatInterrupt("主动中断");
        }
    }

    @Override
    public void sendMessage(User user, String message) {
        ManagedEmitter managedEmitter = getEmitter(user);
        try {
            managedEmitter.getEmitter().send(message);
        } catch (IOException e) {
            removeEmitter(user);
            log.error("发送信息失败");
        }

    }

    @Override
    public ManagedEmitter getEmitter(User user) {
        Integer schoolKey = (Integer) RedisUtils.hashGet(UNIVERSITY_ALL, user.getUniversity());
        ManagedEmitter managedEmitter = getEmitter(schoolKey, generateKey(user));
        if (Objects.isNull(managedEmitter)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "没有存在的sse连接");
        }
        return managedEmitter;
    }

    @Override
    public ManagedEmitter getEmitter(Integer schoolKey, String key) {
        ConcurrentHashMap<String, ManagedEmitter> schoolMap = emitters.get(schoolKey);
        if (Objects.isNull(schoolMap)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "没有存在的sse连接");
        }
        ManagedEmitter managedEmitter = schoolMap.get(key);
        if (Objects.isNull(managedEmitter)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "没有存在的sse连接");
        }
        return managedEmitter;
    }

    @Override
    public String generateKey(User user) {
        return "sse:"+user.getId();
    }

    @Override
    public String generateKey(Long userId) {
        return "sse:"+userId;
    }

    @Override
    public void removeEmitter(User user) {
        Integer schoolKey = (Integer) RedisUtils.hashGet(UNIVERSITY_ALL, user.getUniversity());
        removeEmitter(schoolKey, generateKey(user));
    }

    @Override
    public void removeEmitter(Integer schoolKey, String key) {
        ConcurrentHashMap<String, ManagedEmitter> schoolMap = emitters.get(schoolKey);
        if (Objects.isNull(schoolMap)) {
            return;
        }
        schoolMap.remove(key);
    }

    @Override
    public void sendAllUserMessage(Integer schoolKey, String message) {
        ConcurrentHashMap<String, ManagedEmitter> schoolMap = emitters.get(schoolKey);
        if (Objects.isNull(schoolMap)) {
            return;
        }
        schoolMap.forEach((key, managedEmitter) -> {
            try {
                managedEmitter.getEmitter().send(message);
            } catch (IOException e) {
                e.printStackTrace();
                removeEmitter(schoolKey, key);
            }
        });
    }

    @Override
    public void sendGroupMessage(Integer schoolKey, List<Long> userIds, String message) {
        ConcurrentHashMap<String, ManagedEmitter> schoolMap = emitters.get(schoolKey);
        if (Objects.isNull(schoolMap)) {
            return;
        }
        for (Long userId : userIds) {
            ManagedEmitter managedEmitter = schoolMap.get(generateKey(userId));
            if (Objects.isNull(managedEmitter)) {
                continue;
            }
            try {
                managedEmitter.getEmitter().send(message);
            } catch (IOException e) {
                e.printStackTrace();
                removeEmitter(schoolKey, generateKey(userId));
            }
        }
    }
}
